home *** CD-ROM | disk | FTP | other *** search
/ Programming Sound Cards / Programming Sound Cards.iso / sound_56 / dmastp12.asm < prev    next >
Assembly Source File  |  1995-01-01  |  20KB  |  527 lines

  1. ;══════════════════════════════════════════════════════════════════════════════
  2. ; Play with Cxh/Bxh commands on SoundBlaster 16/16ASP
  3. ;   André Baresel (with some help from Craig Jackson)
  4. ;──────────────────────────────────────────────────────────────────────────────
  5. ; STATUS: DOES WORK ON SB16
  6. ;──────────────────────────────────────────────────────────────────────────────
  7. ; Requirements: 80286, SoundBlaster 16/16ASP (see BASEADDR,DMA channel,IRQ number)
  8. ; Resolutions : 16-bit / 4..44khz / stereo
  9. ; Notes : ■ Today we use 8bit DMA channel for playing 16bit sound
  10. ;         ■ 8bit DMA-16bit sound mode is set by this program (remember what CL writes
  11. ;           about this) and it also restores old setup after all (look at mixerreg 81h)
  12. ;         ■ We use only 8 bit stereo data and convert it while playing
  13. ;           into stereo 16bit, look at CONVERT_HALF (at the end of this file)
  14. ;         ■ To creat a 8 bit stereo unsigned file do :
  15. ;           "VOC2RAW TEST1.VOC /I /R"
  16. ;
  17. ;
  18. ; ■ DSP command 40h  ... set sample rate
  19. ; ■ DSP command D5h  ... Halt Autoinit 8 bit DMA operation
  20. ; ■ DSP command D6h  ... Continue Autoinit 8 bit DMA operation
  21. ; ■ DSP command C6h 00h ... autoinit 16 bit mono data with no sign
  22. ;
  23.  
  24. .MODEL small
  25. .286
  26. ; CONSTANTS ───────────────────────────────────────────────────────────────────
  27.  
  28. ; SoundBlaster SETUP
  29. BASEADDR           EQU 0220h       ;SoundBlaster base address
  30. IRQ7               EQU 15          ;SoundBlaster IRQ
  31. DMAchannel         EQU 1           ;SoundBlaster DMA channel
  32.  
  33. ; PIC MASKS FOR MASK/DEMASK IRQ
  34. PICANDMASK         EQU 01111111b   ;'AND' PIC mask for clear IRQ7
  35. PICORMASK          EQU 10000000b   ;'OR' PIC mask for set IRQ7
  36.  
  37. ; DMA CONTROLLER REGISTERS :
  38. WRITEMASK          EQU 00ah         ;WRITE MASK REGISTER
  39. WRITEMODE          EQU 00bh         ;WRITE MODE REGISTER
  40. CLEARFLIPFLOP      EQU 00ch
  41. PAGE_CHN           EQU 083h         ;PAGE REGISTER FOR DMAchannel 1
  42. BASE_CHN           EQU 002h         ;BASEADDRESS REGISTER DMAchannel 1
  43. COUNT_CHN          EQU 003h         ;COUNT REGISTER DMAchannel 1
  44.  
  45. ; SAMPLERATE :
  46. RATE               EQU 02AEDh       ; = 10989 Hz
  47.  
  48. ; DMA WRITE MODE
  49. WANTEDMODE         EQU 01011000b    ; singlemode, autoinit, readmode
  50.  
  51. ; DMABUFFER SIZE :
  52. DMABUFFERSIZE      EQU 8*1024
  53.  
  54. ;──────────────────────────────────────────────────────────────────────────────
  55. ; MACRO DEFINITIONs
  56. ;──────────────────────────────────────────────────────────────────────────────
  57. STARTUP                 MACRO
  58. ; MASM 5.x COMPATIBILITY
  59. __start:                mov     ax,DGROUP
  60.                         mov     ds,ax
  61.                         mov     bx,ss
  62.                         sub     bx,ax
  63.                         shl     bx,004h
  64.                         mov     ss,ax
  65.                         add     sp,bx
  66. ENDM
  67.  
  68. WAITWRITE               MACRO
  69. LOCAL                   loopWait,endloop
  70. ;          Arguments : DX = Status port (BASEADDR+0Ch)
  71. ;          Returns   : n/a
  72. ;          Destroys  : AL
  73.  
  74.                         push    cx
  75.                         xor     cx,cx           ; need that for slow SBs !
  76. loopWait:               dec     cx
  77.                         jz      endloop
  78.                         in      al,dx           ; AL = WRITE COMMAND STATUS
  79.                         or      al,al
  80.                         js      loopWait        ; Jump if bit7=1 - writing not allowed
  81. endloop:                pop     cx
  82. ENDM
  83.  
  84. WAITREAD                MACRO
  85. LOCAL                   loopWait,endloop
  86. ;          Arguments : DX = Status port   (normaly BASEADDR+0Eh)
  87. ;          Returns   : n/a
  88. ;          Destroys  : AL
  89.  
  90.                         push    cx
  91.                         xor     cx,cx           ; need that for slow SBs !
  92. loopWait:               dec     cx
  93.                         jz      endloop
  94.                         in      al,dx           ; AL = DATA AVAILABLE STATUS
  95.                         or      al,al
  96.                         jns     loopWait        ; Jump if bit7=0 - no data available
  97. endloop:                pop     cx
  98. ENDM
  99.  
  100. RESET_DSP               MACRO
  101. local                   SBthere
  102. ;          Arguments : n/a
  103. ;          Returns   : n/a
  104. ;          Destroys  : DX,AL
  105.  
  106.                         mov      dx,BASEADDR+06h
  107.                         mov      al,1
  108.                         out      dx,al          ; start DSP reset
  109.  
  110.                         in       al,dx
  111.                         in       al,dx
  112.                         in       al,dx
  113.                         in       al,dx          ; wait 3 µsec
  114.  
  115.                         xor      al,al
  116.                         out      dx,al          ; end DSP Reset
  117.  
  118.                         add      dx,08h         ; dx = DSP DATA AVAILABLE
  119.                         WAITREAD
  120.                         sub      dx,4           ; dx = DSP Read Data
  121.                         in       al,dx
  122.                         cmp      al,0aah        ; if there is a SB then it returns 0AAh
  123.                         je       SBthere
  124.                         jmp      RESET_ERROR    ; No SB - exit program
  125. SBthere:
  126. ENDM
  127.  
  128. READMIXER  MACRO register
  129.            mov      dx,BASEADDR+04h
  130.            mov      al,register
  131.            out      dx,al
  132.            inc      dx
  133.            in       al,dx
  134. ENDM
  135.  
  136. WRITEMIXER  MACRO register
  137.             mov       dx,BASEADDR+04h
  138.             mov       al,register
  139.             out       dx,ax
  140. ENDM
  141. ;─── End of Macrodefinitions ──────────────────────────────────────────────────
  142.  
  143.  
  144. .STACK 100h
  145.  
  146. .DATA
  147. ;──────────────────────────────────────────────────────────────────────────────
  148. ; Creat TEST1.INC with calling "VOC2RAW TEST1.VOC /I" or creat your own
  149. ; textfile with sampledata
  150.  
  151. SAMPLEBUFFER LABEL BYTE
  152.     INCLUDE TEST1.INC
  153. SAMPLEBUFFEREND LABEL BYTE
  154.  
  155.     PART                db 0
  156.  
  157.     information         db 13,10,'DMASTP12.EXE - play 16bit mono unsigned data with 8bit DMA (that does only'
  158.                         db 13,10,'word on a SB16/SB16ASP)'
  159.                         db 13,10,'Pause playing with key "p" and continue it then with any key.'
  160.                         db 13,10,'Stop playing with <ESC>.',10,'$'
  161.     memerror            db 13,10,'Not enough memory to creat the DMA buffer','$'
  162.     txtpart0            db 13,'playing part 0','$'
  163.     txtpart1            db 13,'playing part 1','$'
  164.     sberror             db 13,10,'No SoundBlaster at this BASEADDR ! PROGRAM HALTED.','$'
  165.  
  166.     OLDInterruptSEG     dw ?
  167.     OLDInterruptOFS     dw ?
  168.  
  169.     ; Offset and Page for DMAC
  170.     DMAbufferOFS        dw ?
  171.     DMAbufferPage       db ?
  172.     ; Offset and Segment for CPU access :)
  173.     DMABufferDOSOFS     dw ?
  174.     DMABufferDOSSEG     dw ?
  175.  
  176.     ; position in samplebuffer while converting
  177.     position            dw 0
  178.  
  179.     ; OLD SELECT DMA (to restore it)
  180.     OLDselectDMA        db 0
  181.  
  182.     SAMPLEBUFFERLENGTH = offset SAMPLEBUFFEREND - offset SAMPLEBUFFER
  183. ;──────────────────────────────────────────────────────────────────────────────
  184. .CODE
  185.  STARTUP
  186.  
  187.            ; FIRST FREE NOT USED MEMORY :
  188.            mov     bx,ss
  189.            mov     ax,es
  190.            sub     bx,ax
  191.  
  192.            mov     ax,sp
  193.            add     ax,15
  194.  
  195.            shr     ax,4
  196.  
  197.            add     bx,ax
  198.            mov     ah,04ah
  199.            int     21h
  200.  
  201.            ; NOW ALLOCATE DMABUFFER
  202.            mov     bx,SAMPLEBufferlength
  203.            shr     bx,3            ; cx = samplebufferlength*2/16
  204.                                    ; (count of 16byte blocks)
  205.            mov     ah,48h
  206.            int     21h
  207.            jnc     enoughmem       ; ok got the memory
  208.            mov     dx,offset memerror
  209.            mov     ah,9
  210.            int     21h             ; WRITE MSG 2 SCRN THAT THERE'S NOT ENOUGH MEM
  211.            jmp     return2dos
  212. enoughmem: ; AX = segment of DMA buffer / offset = 0
  213.  
  214. ;──────────────────────────────────────────────────────────────────────────────
  215. ; calculate page and offset for DMAcontroller :
  216. ;
  217. ; segment*16+offset = 20bit memory location -> upper 4 bits  = page
  218. ;                                              lower 16 bits = offset
  219. ;──────────────────────────────────────────────────────────────────────────────
  220.            rol     ax,4
  221.            mov     bl,al
  222.            and     bl,00fh
  223.            and     al,0f0h
  224.            mov     [DMABufferOFS],ax
  225.            mov     [DMAbufferPage],bl
  226. ;──────────────────────────────────────────────────────────────────────────────
  227. ; check for DMApage override :
  228. ; ... problem: DMA controller separates memory into 64KB pages, you can only
  229. ; transfer data is placed in one page - no page overrides are allowed
  230. ;──────────────────────────────────────────────────────────────────────────────
  231. ; To solve that :
  232. ; creat a DMA buffer with double size you want - if the first part is placed
  233. ; on a page border the second part is for sure not
  234. ;──────────────────────────────────────────────────────────────────────────────
  235.            mov     cx,SAMPLEBUFFERLENGTH
  236.            neg     ax          ; ax = 65536 - ax   (bytes left to DMA page border)
  237.            cmp     ax,cx
  238.            ja      nooverride
  239.  
  240.            ; USE SECOND PART :
  241.            neg     ax               ; ax = offset first data
  242.            add     ax,cx            ; use second part
  243.            inc     ax             ; cx+1 bytes to next part ;)
  244.            mov     [DMABufferOFS],ax
  245.            inc     [DMABufferPage]  ; 2nd part is on next page !
  246. nooverride:
  247.  
  248. ;──────────────────────────────────────────────────────────────────────────────
  249. ; now fill the  whole buffer with first words of data
  250. ; (2 times CALL CONVERT_HALF)
  251. ;
  252. ; but first - calculate the DOS SEG/OFS from the DMAPage/OFS (you know
  253. ; maybe we have to use second buffer half we don't know about ofs/seg yet)
  254.  
  255.            mov     al,byte ptr [DMABufferOFS]
  256.            and     al,0Fh
  257.            xor     ah,ah
  258.            mov     di,ax       ; di = offset of DMAbuffer
  259.            mov     ax,[DMABufferOFS]
  260.            and     al,0f0h
  261.            or      al,[DMABufferPage]
  262.            ror     ax,4
  263.            mov     es,ax       ; es = segment of DMABuffer
  264. ;──────────────────────────────────────────────────────────────────────────────
  265. ; save these values for later CONVERT_HALF calls
  266. ;
  267.            mov     [DMABufferDOSOFS],di
  268.            mov     [DMABufferDOSSEG],ax
  269.            xor     ax,ax
  270.  
  271.            ; DS:SI - samples in dataseg
  272.            ; ES:DI - DMABuffer
  273.  
  274.            ; fill the whole buffer with sample data
  275.            CALL    CONVERT_HALF
  276.            CALL    CONVERT_HALF
  277.  
  278.            ; NOW WE'RE READY FOR SB STUFF:
  279.  
  280.            ; FIRST SETUP DMA SELECT for 16bit sound with 8bit dma :
  281.            READMIXER 81h
  282.            mov     [OLDselectDMA],al
  283.            and     al,0fh              ; setup only 8bit DMA channel (that means 16bit sound with 8bit DMA
  284.                                        ; big thxs Craig for that info !)
  285.            mov     ah,al
  286.            WRITEMIXER 81h
  287.  
  288.            RESET_DSP
  289.  
  290.            ; NOW WE'RE READY FOR SB STUFF:
  291.            mov     dx,offset information
  292.            mov     ah,9
  293.            int     21h                  ; write program information to screen
  294.  
  295.            ; ENABLE SB SPEAKERS (for all SBs <SB16)
  296.            mov     dx,BASEADDR+00Ch            ;DX = DSP Write Data or Command
  297.            WAITWRITE
  298.            mov     al,0D1h                     ; AL = Enable speaker
  299.            out     dx,al                       ; Output: DSP Write Data or Command
  300.  
  301.            ; SETUP IRQ :
  302.            xor     ax,ax
  303.            mov     es,ax                       ; es to page 0 (Interrupt table)
  304.            mov     si,IRQ7*4                   ; si = position in interrupt table
  305.  
  306.            ; DISABLE IRQ (if it was enabled somehow)
  307.            in      al,021h
  308.            and     al,PICANDMASK               ; SET MASK REGISTER BIT TO DISABLE INTERRUPT
  309.            out     021h,al
  310.  
  311.            ; CHANGE POINTER IN INTERRUPT TABLE
  312.            mov     ax,es:[si]
  313.            mov     [OLDInterruptOFS],ax        ; save offset of old interupt vector for restoring
  314.            mov     ax,OFFSET OWN_IRQ
  315.            mov     es:[si],ax                  ; set offset of new interrupt routine
  316.            mov     ax,es:[si+2]
  317.            mov     [OLDInterruptSEG],ax        ; save segment of old interupt vector for restoring
  318.            mov     ax,cs
  319.            mov     es:[si+2],ax                ; set segment of new interrupt routine
  320.  
  321.            ; CHANGE PIC MASK :
  322.            in      al,021h
  323.            and     al,PICANDMASK   ; CLEAR MASK REGISTER BIT TO ENABLE INTERRUPT
  324.            out     021h,al
  325.  
  326.            mov     cx,DMABUFFERsize-1
  327. ;──────────────────────────────────────────────────────────────────────────────
  328. ; Setup DMA-controller :
  329. ;
  330. ; 1st  MASK DMA CHANNEL
  331. ;
  332.            mov     al,DMAchannel
  333.            add     al,4
  334.            out     WRITEMASK,al
  335. ;──────────────────────────────────────────────────────────────────────────────
  336. ; 2nd  CLEAR FLIPFLOP
  337. ;
  338.            out     CLEARFLIPFLOP,al
  339. ;──────────────────────────────────────────────────────────────────────────────
  340. ; 3rd  WRITE TRANSFER MODE
  341. ;
  342.            mov     al,WANTEDMODE
  343.            add     al,DMAchannel
  344.            out     WRITEMODE,al
  345. ;──────────────────────────────────────────────────────────────────────────────
  346. ; 4th  WRITE PAGE NUMBER
  347. ;
  348.            mov     al,[DMAbufferPage]
  349.            out     PAGE_CHN,al
  350. ;──────────────────────────────────────────────────────────────────────────────
  351. ; 5th  WRITE DMA BASEADDRESS
  352. ;
  353.            mov     ax,[DMABufferOFS]
  354.            out     BASE_CHN,al
  355.            mov     al,ah
  356.            out     BASE_CHN,al
  357. ;──────────────────────────────────────────────────────────────────────────────
  358. ; 6th  WRITE BASECOUNTER = SAMPLELENGTH-1
  359. ;
  360.            mov     al,cl
  361.            out     COUNT_CHN,al
  362.            mov     al,ch
  363.            out     COUNT_CHN,al
  364. ;──────────────────────────────────────────────────────────────────────────────
  365. ; 7th  DEMASK CHANNEL
  366. ;
  367.            mov     al,DMAchannel
  368.            out     WRITEMASK,al
  369.  
  370. ;──────────────────────────────────────────────────────────────────────────────
  371. ; Setup SoundBlaster :
  372. ;
  373. ; 1st  SET SAMPLERATE
  374. ;
  375.            mov     dx,BASEADDR+00Ch            ;DX = DSP Write Data or Command
  376.            WAITWRITE
  377.            mov     al,041h                     ;AL = Set DAC Samplerate
  378.            out     dx,al
  379.            WAITWRITE
  380.            mov     cx,RATE
  381.            mov     al,ch
  382.            out     dx,al
  383.            WAITWRITE
  384.            mov     al,cl
  385.            out     dx,al
  386.  
  387. ;──────────────────────────────────────────────────────────────────────────────
  388. ; 2nd  USE 16bit MONO UNSIGNED MODE (DSPC B6h 00h)
  389. ;
  390.            WAITWRITE
  391.            mov     al,0B6h                     ;AL = DMA DAC 16bit autoinit
  392.            out     dx,al
  393.            WAITWRITE
  394.            mov     al,000h                     ;AL = mono unsigned data
  395.            out     dx,al
  396.            mov     cx,DMABUFFERSIZE/4-1
  397.            WAITWRITE
  398.            mov     al,cl                       ;AL = LOWER PART SAMPLELENGTH
  399.            out     dx,al
  400.            WAITWRITE
  401.            mov     al,ch                       ;AL = HIGHER PART SAMPLELENGTH
  402.            out     dx,al
  403.  
  404. ; TRANSFER STARTs.....NOW.... :)
  405.  
  406. waitloop:  mov     ah,01                       ;AH = Check for character function
  407.            int     016h                        ;   Interrupt: Keyboard
  408.            jz      waitloop                    ; wait for a key (sound in background)
  409.  
  410.            xor     ah,ah                       ;Read character, flush keypress
  411.            int     016h                        ;   Interrupt: Keyboard
  412.            cmp     al,'p'                      ; check for pause key
  413.            je      pause                       ; ok
  414.            cmp     al,27
  415.            jne     waitloop
  416.            jmp     exit
  417.  
  418. pause:     ; NOW PAUSE PLAYING: (on DSPv4.04 you can also use DSPC d4h,d0h!)
  419.            mov     dx,BASEADDR+00Ch            ;DX = DSP Write Data or Command
  420.            WAITWRITE
  421.            mov     al,0D5h
  422.            out     dx,al
  423.  
  424.            ; WAIT FOR ANY KEY ("<ANY> key?" shut up it's a stupid joke!)
  425.            xor     ah,ah                       ;Read character, flush keypress
  426.            int     016h                        ;   Interrupt: Keyboard
  427.  
  428.            mov     dx,BASEADDR+00Ch            ;DX = DSP Write Data or Command
  429.            WAITWRITE
  430.            mov     al,0d6h
  431.            out     dx,al
  432.  
  433.            jmp     waitloop
  434.  
  435. exit:      RESET_DSP
  436.  
  437.            ; RESTORE PIC MASK
  438.            in      al,021h
  439.            or      al,PICORMASK                ;<-- SET REGISTER MASK BITS TO DISABLE
  440.            out     021h,al
  441.  
  442.            ; RESTORE IRQ :
  443.            xor     ax,ax
  444.            mov     es,ax                       ; es to page 0 (Interrupt table)
  445.            mov     si,IRQ7*4
  446.            mov     ax,[OLDInterruptOFS]
  447.            mov     es:[si],ax                  ; set old interrupt routine
  448.            mov     ax,[OLDInterruptSEG]
  449.            mov     es:[si+2],ax
  450.  
  451.            ; CLEAR KEYBUFFER
  452.            mov     ah,01
  453.            int     16h
  454.            jz      return2dos
  455.            xor     ah,ah                       ;Read character, flush keypress
  456.            int     016h                        ;   Interrupt: Keyboard
  457.  
  458.            ; TERMINATE EXE:
  459. return2dos:
  460.            mov     ax,04c00h
  461.            int     21h
  462.  
  463. ; display information if Soundblaster is not on this baseaddress
  464. RESET_ERROR:
  465.            mov     dx,offset sberror
  466.            mov     ah,9
  467.            int     21h                         ; text output
  468.            jmp     return2dos
  469.  
  470. ;──────────────────────────────────────────────────────────────────────────────
  471. ; Our own IRQ for detecting buffer half SB currently plays
  472. ; It's generated by the SoundBlaster hardware
  473. ;──────────────────────────────────────────────────────────────────────────────
  474. OWN_IRQ:   pusha
  475.            mov     dx,BASEADDR+00Fh            ;DX = IRQ ACKNOWLEDGE 16Bit
  476.            in      al,dx
  477.            mov     ax,@data
  478.            mov     ds,ax
  479.            mov     dx,offset txtpart0
  480.            cmp     [part],0
  481.            je      notpart1
  482.            mov     dx,offset txtpart1
  483. notpart1:  mov     ah,9
  484.            int     21h             ; text output
  485.            call    CONVERT_HALF    ; fill next half...
  486.            mov     al,020h
  487.            out     020h,al                     ;ACKNOWLEDGE HARDWARE INTERRUPT
  488.            popa
  489.            IRET
  490.  
  491. ;──────────────────────────────────────────────────────────────────────────────
  492. ; Convert_half is for copying 8bit data from dataseg to dmabuffer with 16bit
  493. ; values (16bitvalue= 8bit value*256)
  494. ; one call - convert one buffer half
  495. ; next call - convert the other buffer half
  496. ; ... 2B Continued ...
  497. ;──────────────────────────────────────────────────────────────────────────────
  498.  
  499. CONVERT_HALF:
  500.            mov     cx,DMABUFFERSIZE/2            ; half buffer size in bytes
  501.            mov     di,[DMABufferDOSOFS]
  502.            cmp     [part],0
  503.            je      not2nd
  504.            add     di,cx
  505. not2nd:    shr     cx,1                        ; count of words in half buffer
  506.            mov     ax,[DMABufferDOSSEG]
  507.            mov     es,ax
  508.            mov     si,offset samplebuffer
  509.            add     si,[position]
  510.            xor     al,al
  511. cloop:     mov     ah,ds:[si]
  512.            stosw
  513.            inc     si
  514.            cmp     si,offset samplebuffer + samplebufferlength-2
  515.            jb      notsamplerestart
  516.            ; restart sample
  517.            mov     si,offset samplebuffer
  518. notsamplerestart:
  519.            loop    cloop
  520.            sub     si,offset samplebuffer
  521.            mov     [position],si
  522.            neg     [part]
  523.            inc     [part]      ; part = 1-part  result: 0,1,0,1,0,1,...
  524.            RET
  525.  
  526. END     __start
  527.